using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;

public abstract class LoadableJSONObject : ILoadableJsonInterface
{
	public string name;
	public string description;
	public bool builtIn = false;
    public abstract void Load(string loadCode, string fileName);
	public abstract string ToJSON();
	public string fileName;
}

public interface ILoadableJsonInterface
{
	string ToJSON();
}

public static class HelperFunctions {
	
	public static int Modifier(this int input)
	{
		return Mathf.FloorToInt((input-10)/2);
	}
	
	public static int Sign(this int input)
	{
		if(input < 0)
			return -1;
		else
			return 1;
	}
	
	public static string ToJSON(this List<CharacterClass> input)
	{
		List<string> JsonList = new List<string>();
		foreach(LoadableJSONObject ljo in input)
		{
			JsonList.Add(ljo.ToJSON());
		}
		return MiniJSON.Json.Serialize(JsonList);
	}
	
	public static string ToJSON(this List<Ability> input)
	{
		List<string> JsonList = new List<string>();
		foreach(LoadableJSONObject ljo in input)
		{
			JsonList.Add(ljo.ToJSON());
		}
		return MiniJSON.Json.Serialize(JsonList);
	}
	
	public static string ToJSON(this List<InventoryItem> input)
	{
		List<string> JsonList = new List<string>();
		foreach(LoadableJSONObject ljo in input)
		{
			JsonList.Add(ljo.ToJSON());
		}
		return MiniJSON.Json.Serialize(JsonList);
	}
	
	public static string ToJSON(this List<ProtectiveItem> input)
	{
		List<string> JsonList = new List<string>();
		foreach(LoadableJSONObject ljo in input)
		{
			JsonList.Add(ljo.ToJSON());
		}
		return MiniJSON.Json.Serialize(JsonList);
	}
	
	public static string ToJSON(this List<Skill> input)
	{
		List<string> JsonList = new List<string>();
		foreach(LoadableJSONObject ljo in input)
		{
			JsonList.Add(ljo.ToJSON());
		}
		return MiniJSON.Json.Serialize(JsonList);
	}
	
	public static string ToJSON(this List<Spell> input)
	{
		List<string> JsonList = new List<string>();
		foreach(LoadableJSONObject ljo in input)
		{
			JsonList.Add(ljo.ToJSON());
		}
		return MiniJSON.Json.Serialize(JsonList);
	}
	
	public static string ToJSON(this List<WeaponItem> input)
	{
		List<string> JsonList = new List<string>();
		foreach(LoadableJSONObject ljo in input)
		{
			JsonList.Add(ljo.ToJSON());
		}
		return MiniJSON.Json.Serialize(JsonList);
	}
	
	public static string ToJSON(this List<BuffDebuff> input)
	{
		List<string> JsonList = new List<string>();
		foreach(LoadableJSONObject ljo in input)
		{
			JsonList.Add(ljo.ToJSON());
		}
		return MiniJSON.Json.Serialize(JsonList);
	}
	
	public static string ToJSON(this List<TemporaryBonus> input)
	{
		List<string> JsonList = new List<string>();
		foreach(TemporaryBonus tb in input)
		{
			JsonList.Add(tb.ToJSON());
		}
		return MiniJSON.Json.Serialize(JsonList);
	}
	
	public static int AddUpTotals(this Dictionary<string, List<TemporaryBonus>> input)
	{
		int val = 0;
		
		foreach(List<TemporaryBonus> lb in input.Values)
		{
			lb.ForEach(b => 
			{
				if(b.dicep == 0)
					val += b.amount;
			});
		}
		
		return val;
	}
	
	public static Dictionary<string, int> SubTotals(this Dictionary<string, List<TemporaryBonus>> input)
	{
		Dictionary<string,int> val = new Dictionary<string, int>();
		
		foreach(List<TemporaryBonus> lb in input.Values)
		{
			lb.ForEach(b => 
			{
				if(b.dicep == 0)
				{
					if(val.ContainsKey(b.type))
					{
						val[b.type] += b.amount;
					}
					else
						val.Add(b.type, b.amount);
				}
			}
			);
		}
		
		return val;
	}
	
	public static Dictionary<string,string> ToDSS(this Dictionary<string,object> input)
	{
		Dictionary<string,string> dss = new Dictionary<string, string>();
		foreach(KeyValuePair<string,object> k in input)
		{
			dss.Add(k.Key, k.Value as string);
		}
		
		return dss;
	}
	
	public static string Description(this List<RollDefinition> input)
	{
		string desc = "";
		
		Dictionary<string, int> bonuses = new Dictionary<string, int>();
		
		foreach(RollDefinition roll in input)
		{
			if(roll.dieCount == 0)
			{
				if(bonuses.ContainsKey(roll.type))
					bonuses[roll.type] += roll.bonusAmount;
				else
					bonuses.Add(roll.type, roll.bonusAmount);
			}
			else
			{
				desc += " + " + roll.dieCount + "d" + roll.dieType;
				if(roll.bonusAmount != 0)
					desc += ((roll.bonusAmount > 0)?" + ":" - ") + Mathf.Abs(roll.bonusAmount);
				if(!string.IsNullOrEmpty(roll.type))
					desc += " (" + roll.type + ")";
			}
		}
		
		desc = desc.Trim(new char[]{' ','+'});
		
		foreach(string bonusType in bonuses.Keys)
		{
			desc += ((bonuses[bonusType] > 0)?" + ":" - ") + Mathf.Abs(bonuses[bonusType]);
			desc += " (" + bonusType + ")";
		}
		
		return desc;
	}
	
	public static string Roll(this List<RollDefinition> input, RollWindowClass rollWindow)
	{
		string r = "";
		
		bool critical = false;
		
		Dictionary<string, int> totals = new Dictionary<string, int>();
		
		foreach(RollDefinition roll in input)
		{
			if(!totals.ContainsKey(roll.type))
				totals.Add(roll.type, 0);
			
			int result = UnityEngine.Random.Range(roll.dieCount, roll.dieCount * roll.dieType + 1);
			if(roll.dieCount == 1 && roll.dieType == 20 && roll.type == "")
			{
				if(result == 20 || (rollWindow.weapon != null && result >= rollWindow.weapon.critBounds))
					critical = true;
				
				rollWindow.rawDieResult = result;
			}
			
			if(roll.dieCount > 0)
				totals[roll.type] += result;
			
			totals[roll.type] += roll.bonusAmount;
		}
		
		int total = 0;
		foreach(int i in totals.Values)
		{
			total += i;
		}
		
		if(totals.Count > 1)
		{
			r += total + ": ";
			
			string subDesc = "";
			foreach(string type in totals.Keys)
			{
				subDesc += ((totals[type] >= 0)?" + ":" ") + totals[type];
				//if(type != "")
					//subDesc += "(" + type + ")";
			}
			
			r += subDesc.TrimStart(new char[]{' ','+'});
		}
		else
			r += total;
		
		if(critical)
			r += " (Critical!)";
		
		return r;
	}
	
	public static IEnumerator SetterWindow(string windowDescription, List<SetterWindowDescription> variables)
	{
		GameReference reference = GameObject.FindObjectOfType(typeof(GameReference)) as GameReference;
		GameObject window = GameObject.Instantiate(reference.SetterWindowPrefab) as GameObject;
		
		window.transform.parent = reference.BaseMenuPanel;
		window.transform.localScale = reference.SetterWindowPrefab.transform.localScale;
		
		BrowserManager.currentNewTransform = window.transform;
		window.transform.localPosition = BrowserManager.CurrentIndex;
		
		SetterWindowClass windowScript = window.GetComponent<SetterWindowClass>();
		windowScript.LoadDescription(windowDescription, variables);
		while(window != null)
		{
			yield return new WaitForSeconds(0.1f);
		}
		
		yield break;
	}
	
	public static void ButtonWindow(string windowDescription, List<SetterWindowDescription> variables, string callback, GameObject target, out GameObject windowOut)
	{
		GameReference reference = GameObject.FindObjectOfType(typeof(GameReference)) as GameReference;
		GameObject window = GameObject.Instantiate(reference.SetterWindowPrefab) as GameObject;
		
		window.transform.parent = reference.BaseMenuPanel;
		window.transform.localScale = reference.SetterWindowPrefab.transform.localScale;
		BrowserManager.currentNewTransform = window.transform;
		window.transform.localPosition = BrowserManager.CurrentIndex;
		
		SetterWindowClass windowScript = window.GetComponent<SetterWindowClass>();
		windowScript.LoadDescription(windowDescription, variables, callback, target);
		
		windowOut = window;
	}
	
	public static IEnumerator ConfirmationWindow(string windowDescription, List<bool> outBools)
	{
		GameReference reference = GameObject.FindObjectOfType(typeof(GameReference)) as GameReference;
		GameObject window = GameObject.Instantiate(reference.ConfirmationWindowPrefab) as GameObject;
		
		window.transform.parent = reference.BaseMenuPanel;
		window.transform.localScale = reference.SetterWindowPrefab.transform.localScale;
		
		BrowserManager.currentNewTransform = window.transform;
		window.transform.localPosition = BrowserManager.CurrentIndex;
		
		SetterWindowClass windowScript = window.GetComponent<SetterWindowClass>();
		
		windowScript.LoadSimpleConfirmation(windowDescription, outBools);
		
		while(window != null)
		{
			yield return new WaitForSeconds(0.1f);
		}
		
		yield return new WaitForSeconds(0);
	}
	
	public static IEnumerator HealthWindow(CharacterSheet character)
	{
		GameReference reference = GameObject.FindObjectOfType(typeof(GameReference)) as GameReference;
		GameObject window = GameObject.Instantiate(reference.HealthWindowPrefab) as GameObject;
		
		window.transform.parent = reference.BaseMenuPanel;
		window.transform.localScale = reference.SetterWindowPrefab.transform.localScale;
		
		BrowserManager.currentNewTransform = window.transform;
		window.transform.localPosition = BrowserManager.CurrentIndex;
		
		HealthWindowClass windowScript = window.GetComponent<HealthWindowClass>();
		windowScript.LoadCharacter(character);
		while(window != null)
		{
			yield return new WaitForSeconds(0.1f);
		}
		
		yield return new WaitForSeconds(0); 
	}
	
	public static IEnumerator RollWindow(string type, int bonus)
	{
		List<RollDefinition> rd = new List<RollDefinition>();
		rd.Add(new RollDefinition());
		rd[0].bonusAmount = bonus;
		
		yield return GameReference.reference.StartCoroutine(RollWindow(rd, type));
	}
	
	public static IEnumerator RollWindow(List<RollDefinition> rolls, string type)
	{
		GameReference reference = GameObject.FindObjectOfType(typeof(GameReference)) as GameReference;
		GameObject window = GameObject.Instantiate(reference.RollWindowPrefab) as GameObject;
		
		window.transform.parent = reference.BaseMenuPanel;
		window.transform.localScale = reference.SetterWindowPrefab.transform.localScale;
		
		BrowserManager.currentNewTransform = window.transform;
		window.transform.localPosition = BrowserManager.CurrentIndex;
		
		RollWindowClass windowScript = window.GetComponent<RollWindowClass>();
		
		yield return windowScript.StartCoroutine(windowScript.Roll(rolls, type));
		
		while(window != null)
		{
			yield return new WaitForSeconds(0.1f);
		}
		
		yield break; 
	}
	
	public static IEnumerator RollWindow(List<List<RollDefinition>> rolls, string type, WeaponItem weapon)
	{
		GameReference reference = GameObject.FindObjectOfType(typeof(GameReference)) as GameReference;
		GameObject window = GameObject.Instantiate(reference.RollWindowPrefab) as GameObject;
		
		window.transform.parent = reference.BaseMenuPanel;
		window.transform.localScale = reference.SetterWindowPrefab.transform.localScale;
		
		BrowserManager.currentNewTransform = window.transform;
		window.transform.localPosition = BrowserManager.CurrentIndex;
		
		RollWindowClass windowScript = window.GetComponent<RollWindowClass>();
		
		yield return windowScript.StartCoroutine(windowScript.RollSeries(rolls, type, weapon));
		
		while(window != null)
		{
			yield return new WaitForSeconds(0.1f);
		}
		
		yield return new WaitForSeconds(0); 
	}
	
	public static double Evaluate(string expression)  
	 {  
	     return (double)new System.Xml.XPath.XPathDocument(new System.IO.StringReader("<r/>")).CreateNavigator().Evaluate  
	     (string.Format("number({0})", new  
	     System.Text.RegularExpressions.Regex(@"([\+\-\*])").Replace(expression, " ${1} ").Replace("/", " div ").Replace("%", " mod ")));  
	 }
	
	public enum ObjectType
	{
		BUFF,
		CHARACTER,
		CLASS,
		ABILITY,
		SKILL,
		WEAPON,
		ITEM,
		SPELL,
		RACE,
		SHEET,
		NONE
	}
	
	public static string getRealName(string fileName, ObjectType type)
	{
		string retName = fileName;
		
		try
		{
			switch(type)
			{
			case ObjectType.BUFF:
				retName = BuffDebuffManager.index[retName];
				break;
			case ObjectType.CHARACTER:
				retName = SheetManager.index[retName];
				break;
			case ObjectType.CLASS:
				retName = ClassManager.index[retName];
				break;
			case ObjectType.ABILITY:
				if(FeatManager.index.ContainsKey(retName))
					retName = FeatManager.index[retName];
				else
					retName = SpecialAbilityManager.index[retName];
				break;
			case ObjectType.SKILL:
				retName = SkillManager.index[retName];
				break;
			case ObjectType.WEAPON:
				retName = retName.Trim(new char[]{'$','^'});
				retName = WeaponManager.index[retName];
				break;
			case ObjectType.ITEM:
				retName = retName.Trim(new char[]{'$','^'});
				if(fileName.EndsWith("^"))
					retName = WeaponManager.index[retName];
				else
					retName = ItemManager.index[retName];
				break;
			case ObjectType.SPELL:
				retName = SpellManager.index[retName];
				break;
			case ObjectType.RACE:
				retName = RaceManager.index[retName];
				break;
			case ObjectType.SHEET:
				retName = SheetManager.index[retName];
				break;
			}
		}
		catch
		{
			retName = "Error: Missing File ("+retName+")";
		}
		
		return retName;
	}
}

public class SetterWindowDescription
{
	public string outVariable;
	
	public string label;
	public string defaultValue;
	public bool isLarge;
	public bool multiOptionList = false;
	public List<string> options;
	public bool canSelectMultiple = false;
	
	public SetterWindowDescription(string label, string defaultValue, bool isLarge, List<string> options = null, bool canSelectMultiple = false)
	{
		outVariable = defaultValue;
		this.label = label;
		this.defaultValue = defaultValue;
		this.isLarge = isLarge;
		this.options = options;
		if(options != null)
		{
			multiOptionList = true;
			this.canSelectMultiple = canSelectMultiple;
		}
	}
}
